#!/usr/bin/env python
import logging,os,re,signal,sys,time
import smtplib,subprocess
import MySQLdb
import imp

#read the config file
imp.load_source('cfg' ,'actiond.cfg')
from cfg import *

pid_file = '/var/run/actiond.pid'
log_file = '/var/log/actiond.log'
log_level = logging.INFO

def daemon(stdin='/dev/null', stdout='/dev/null', stderr='/dev/null'):
	try:
		pid = os.fork()
		if pid > 0: 
			#parent exit
			os._exit(0)
	except OSError, e:
		sys.stderr.write("fork #1 failed: (%d) %s\n" % (e.errno, e.strerror))
		os._exit(1)
	#detach from terminal
	os.chdir('/')
	os.setsid()
	os.umask(0)

	#fork again, ensure not be session leader
	try:
		pid = os.fork()
		if pid > 0:
			os._exit(0) # exit parent
	except OSError, e:
		sys.stderr.write("fork #2 failed: (%d) %s\n" % (e.errno, e.strerror))
		os._exit(1)
	#close all open file descriptors
	import resource
	maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1]
	if(maxfd == resource.RLIM_INFINITY):
		maxfd = 1024
	for fd in range(0,maxfd):
		try:
			os.close(fd)
		except OSError: # not open, ignore
			pass
	#redirect standard file descriptors
	si = file(stdin, 'r')
	so = file(stdout, 'a+', 1)
	se = file(stderr,'a+', 0)
	os.dup2(si.fileno(), sys.stdin.fileno())
	sys.stdout = so
	sys.stderr = se

def sendmail(msg):
	header = "From: %s<%s>\r\n" % (fromName, fromAddr)
	header += "To: %s\r\n" % toAddr
	header += "Subject: %s\r\n\r\n" % subject
	
	msg = header + msg

	smtp = smtplib.SMTP()
	smtp.connect()
	smtp.sendmail(fromAddr, toAddr, msg)
	smtp.quit()
	
def get_tasklist(conn):
	try:
		cursor = conn.cursor()
		sql = 'SELECT `id`,`action_id`,`user_id` FROM `history` WHERE `status` = %s'
		cursor.execute(sql,'NEW')
		task = list()	
		row = cursor.fetchone()
		while row:
			(id,action_id,user_id) = row
			#get action name and command
			c = conn.cursor()
			c.execute('SELECT `name`,`command` FROM `action` WHERE `id` = %s', action_id)
			(action_name,action_command) = c.fetchone()
			#get user login
			c.execute('SELECT `login` FROM `user` WHERE `id` = %s', user_id)
			(login,) = c.fetchone()
	
			task.append((id,login,action_name,action_command))
			row = cursor.fetchone()
		cursor.close()
		return task
	except MySQLdb.DatabaseError, e:
		logger.error('getting task from database: %s' % e)
def update_task_status(conn, id, status, output=None, error=None):
	try: 
		c = conn.cursor()
		sql_update = """UPDATE `history` SET `status` = %s, `last_update` = NOW(), 
			`stdout` = %s, `stderr` = %s WHERE `id` = %s"""
		c.execute(sql_update,(status, output, error, id))
		c.close()
	except MySQLdb.DatabaseError, e:
		logger.error('update task status: %s' % e)

def do_task(task, conn):
	id,login,action_name,action_command = task
	logger.info('executing action [%s] task id [%d] for user [%s]' % (action_name,id,login))
	#mark task status as processing
	update_task_status(conn,id,'PROCESSING')
	task_log_file = '/tmp/actiond_%s_%d.log' % (action_name, id)
	try:
		task_log = open(task_log_file,'w',0);
		proc = subprocess.Popen(action_command, shell=True,
			stdout=task_log, stderr=subprocess.PIPE)
		exitcode = proc.wait()
		task_log.close()

		if exitcode == 0:
			logger.info('task id [%d] finished successfully' % id)
			logger.info('check "%s" for more info' % task_log_file)
			update_task_status(conn, id, 'SUCCESS')
		else:
			logger.error('task id [%d] failed:' % id)
			update_task_status(conn,id,'FAIL')
			errormsg = proc.stderr.read()
			logger.error(errormsg)
			sendmail('Error executing action [%s]\n%s' % (action_name, errormsg))
	except IOError, e:
		logger.error('IOError: %s' % e)
	except OSError, e:
		logger.error('OSError: %s' % e)
	
def sigHandler(signum, frame):
	if signum == signal.SIGTERM:
		logger.info('caught TERM signal, exiting...')
		os.unlink(pid_file)
		logger.info('action daemon stopped!')
		sys.exit(0)

def work():
	#create pid file
	pidHandle = open(pid_file, 'w')
	pidHandle.write('%s\n' % os.getpid())
	pidHandle.close()

	logger.info('action daemon started!')

	#monitoring...
	try:
		conn = MySQLdb.connect(db=db_name, user=db_user, passwd=db_pass)
		conn.autocommit(1)
	except MySQLdb.DatabaseError, e:
		logger.error('connect to database: %s' % e)
		return
	while 1:
		tasks = get_tasklist(conn)
		for task in tasks:
			do_task(task, conn)
		time.sleep(2)

if __name__ == "__main__":
	#become a daemon
	daemon(stderr=log_file)
	#install signal handler
	signal.signal(signal.SIGTERM, sigHandler)
	#setup logger
	logger = logging.getLogger('actiond')
	logger.setLevel(log_level)
	handler = logging.StreamHandler()
	handler.setLevel(log_level)
	formatter = logging.Formatter("%(asctime)s %(levelname)s:  %(message)s", "%F %T")
	handler.setFormatter(formatter)
	logger.addHandler(handler)
	if os.path.isdir(script_path):
		os.chdir(script_path)
	work()	
